package cn.base.web.token;

import cn.base.web.cahe.TemporaryCache;
import cn.base.web.lock.LockService;
import cn.rengy.lang.WeChatException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import java.time.Duration;
@Slf4j
public abstract class AbstractTokenService implements TokenService{

	@Autowired
	private TemporaryCache temporaryCache;//临时缓存

    @Autowired
    private LockService lockService;
	//@Autowired
	//private AccessTokenRefreshService accessTokenRefreshService;


	/**
	 * 获取token
	 * @param clientid
	 * @return
	 * @throws Exception
	 */
	public String acquire(String clientid) throws Exception {
		String key=getCacheKey(clientid);
		/**
		 * 1.从缓存中获取
		 * 2.有 返回
		 * 3.无 刷新缓存 再次获取返回
		 */

		String token=(String)temporaryCache.get(key);
		//logger.debug("从缓存中获取{}的accessToken结果{}",key,token);
		if(token==null){
			refresh(clientid);
			token=(String)temporaryCache.get(key);;
		}
		if(token==null){
			throw new TokenException(1,"refresh token失败");
		}
		return token;
	}

	/**
	 * 当token不可用时删除
	 * @param clientid
	 */
	public void evict(String clientid,String value) throws Exception {
        //和获取token使用同一把锁
        lockService.lock(getLockKey(clientid), () -> {
            //判断token是否为空
            String cacheKey=getCacheKey(clientid);
            String token=(String)temporaryCache.get(cacheKey);
            if(token!=null&&token.equals(value)){
                //当token失效时先删除
                temporaryCache.delete(cacheKey);
                //当token刷新后,ticket是否可用？
            }
            return true;
        });




	}
	/**
	 * 刷新Token
	 * @param clientid
	 * @throws Exception
	 */
	public void refresh(String clientid) throws Exception{
        /**
         * 逻辑约定了当执行获取token的时候，redis中的token肯定为空， （1第一次使用2失效过期3返回40001先删除）
         *
         * 所以当获取到锁后只要判断token不为空就可以退出，不执行获取
         */
        //使用redislock分布式锁防止并发执行刷新，必须同时结合token为空判断，之前的方案是判断时间是否最近更新也是可以的

        boolean t=lockService.lock(getLockKey(clientid), () -> {
            //判断token是否为空
            String cacheKey=getCacheKey(clientid);
            String token=(String)temporaryCache.get(cacheKey);
            if(token==null|| token.isEmpty()){
                TokenDTO tokenDTO=newToken(clientid);
                saveToken(cacheKey,tokenDTO);
                //更新access_token后ticket是否有效？无效则删除jsapiticket
                //tokenRedisService.evictJsApiTicket(wx_account_id);
            }
            return true;
        });


	}
	void saveToken(String cacheKey,TokenDTO tokenDTO) {
		//加入缓存 提前120秒刷新 前提 有效期小时级别 ,可以判断时间长短设置刷新时间
        //tokenDTO.getExpires().getSeconds()>30分钟
        temporaryCache.set(cacheKey,tokenDTO.getToken(),Duration.ofSeconds(tokenDTO.getExpires().getSeconds()-120));
		//stringRedisTemplate.opsForValue().set(key, tokenDTO.getToken(),Duration.ofSeconds(tokenDTO.getExpires()-120));
	}


	/**
	 * 获取一个新的token
	 * @param clientid
	 * @return
	 */
	protected abstract TokenDTO newToken(String clientid);


	//abstract void afterSaveToken();
}
